feat(discord): include reply/quote context in agent prompt (#339)#527
feat(discord): include reply/quote context in agent prompt (#339)#527ChunHao-dev wants to merge 3 commits intoopenabdev:mainfrom
Conversation
OpenAB PR ScreeningThis is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.
Screening report## IntentThis PR fixes a Discord conversation-context gap: when a user replies to or quotes an earlier message in a thread, the agent currently only sees the new message text and loses the referenced message content. That makes prompts like “summarize this” or “answer that” ambiguous and degrades response quality for Discord users. FeatThis is a feature-sized fix to Discord prompt assembly. It adds reply/quote context to the agent prompt by resolving the referenced Discord message, formatting it into a quote block, and prepending it to the user’s new message before routing to the agent. Non-reply messages remain unchanged. Who It ServesThe primary beneficiary is Discord end users interacting with OpenAB agents in threads and reply chains. Secondarily, it helps maintainers and reviewers by making agent behavior more predictable and aligned with how users naturally converse in Discord. Rewritten PromptUpdate the Discord adapter so reply messages include the referenced message content in the prompt sent to the agent. Requirements:
Merge PitchThis is worth moving forward because it fixes a real prompt-quality issue in a core user interaction path without changing the broader routing model. The risk profile is low to moderate: behavior is isolated to Discord reply handling, but reviewers will likely want to confirm prompt formatting is stable, API fallback is safe, and quoted context does not introduce noisy or misleading prompt injection in edge cases. Best-Practice ComparisonOpenClaw principles:
Hermes Agent principles:
Overall:
Implementation Options
Comparison Table
RecommendationThe balanced option is the right path for merge discussion. It solves the actual user problem reliably, keeps the change scoped to the Discord adapter, and avoids overbuilding a general context system before the project has validated the need for broader prompt-enrichment rules. If this moves forward, the likely follow-up split is:
|
Fix: resolve mentions in quoted message contentThe quoted message content from Change: Added Some(quoted) => {
let quoted_content = resolve_mentions("ed.content, bot_id);
format_quote_context("ed.author.name, "ed_content, &prompt)
}Minimal change — 4 lines added, 1 removed. |
a3f8f2c to
9dfd955
Compare
masami-agent
left a comment
There was a problem hiding this comment.
PR Review: #527
Summary
- Problem: When a user replies to (quotes) a message in a Discord thread, the quoted content is lost — the agent only sees the new message text with no context about what "this" refers to.
- Approach: Read
msg.referenced_message(gateway-provided, zero cost) with HTTP API fallback, prepend formatted quote block to the prompt before sending to the ACP agent. - Risk level: Low
Core Assessment
- Problem clearly stated: ✅ — well-documented in both issue #339 and PR description
- Approach appropriate: ✅ — two-tier resolution (gateway cache → HTTP fallback) is the correct pattern for serenity 0.12
- Alternatives considered: ✅ — the gateway-first + HTTP-fallback design is explicitly documented
- Best approach for now: ✅ — minimal, focused, non-breaking
Findings
Code correctness:
resolve_referenced_message()correctly handles serenity 0.12 types:message_reference.channel_idisChannelId(non-optional),message_idisOption<MessageId>— the?operator usage is correct.*referenced.clone()dereferences theBox<Message>— correct pattern forOption<Box<Message>>.resolve_mentions()is applied to the quoted content, which correctly strips bot mentions from the quoted text too. Good attention to detail.- Insertion point is correct: after
resolve_mentions()on the user's own message, before the empty-check gate. This means a reply with empty user text but non-empty quoted content will still be processed — which is the right behavior. - The
tracing::warn!on HTTP fetch failure with structured fields follows the project's existing logging pattern.
format_quote_context() design:
- Pure function, easy to test — good separation.
- Empty
quoted_contentreturns prompt unchanged — correct guard. - The format
[Quoted message from @{author_name}]:\n{content}\n\n{prompt}is clean and gives the agent clear context about who said what.
Review Summary
🔧 Suggested Changes
- Consider adding a length cap on quoted content. If someone quotes a very long message (e.g., a full code dump from the bot), the entire thing gets prepended to the prompt. A reasonable truncation (e.g., first 2000 chars with a
[truncated]marker) would prevent unexpectedly large prompts. Not blocking — this can be a follow-up.
ℹ️ Info
- This only handles single-level quoting (the direct
referenced_message). Nested quotes (quoting a message that itself was a reply) won't include the deeper context. This is fine for now — Discord's own UI only shows one level of reply context. - The HTTP fallback path (
channel_id.message(http, message_id)) counts against Discord's rate limit. In practice this should be rare since the gateway almost always includesreferenced_message, but worth noting for awareness.
⚪ Nits
- None — code is clean and well-structured.
Verdict
APPROVE — Clean, focused implementation. Single file changed, 4 unit tests, all 109 existing tests pass, CI green across all 7 smoke-test variants. The code correctly handles serenity 0.12 types, follows existing project patterns, and the insertion point is well-chosen. Ready for maintainer review.
obrutjack
left a comment
There was a problem hiding this comment.
Reviewed. Clean, focused implementation — gateway-first with HTTP fallback, resolve_mentions applied to quoted content, good test coverage. LGTM.
chaodu-agent
left a comment
There was a problem hiding this comment.
🟡 Conditional Approve — Needs rebase, feature is net-new and valuable.
Baseline Check (Step 0)
| Field | Value |
|---|---|
| State | OPEN |
| Mergeable | CONFLICTING |
| Created | 2026-04-22 |
| Labels | pending-screening |
| Changes | reply/quote context injection into agent prompt |
Main branch status: No existing reply/quote handling. Feature is 100% net-new. Conflict is from surrounding discord.rs changes (PR #666, #646), not from overlapping features.
Four-Question Framework
1. What problem does it solve?
When a user replies to (quotes) a message in a Discord thread, the bot only sends the new message text — the quoted content is lost. The agent has no idea what "this" refers to.
2. How does it solve it?
resolve_referenced_message()— prefers gateway-providedreferenced_message(zero cost); falls back to HTTP API call viamessage_referenceformat_quote_context()— pure function that formats the quote block- Injected after
resolve_mentions(), before prompt is sent to router - Non-reply messages completely unaffected
3. What was considered?
Clean separation of async I/O (resolve) and pure formatting (format). Graceful degradation on HTTP fallback failure.
4. Is it the best approach?
Yes — the two-layer resolution (gateway payload → HTTP fallback) is the right pattern. The pure formatting function is well-tested.
Traffic Light
🟢 INFO
- Clean separation of async I/O and pure formatting
- Graceful degradation: HTTP fallback failure logs warning, continues without quote
resolve_mentions()applied to quoted content too- 4 unit tests covering edge cases
- Zero impact on non-reply messages
- Excellent PR description
🟡 NIT
- Content length cap: Long quoted messages (4000+ chars) prepended in full. Consider truncating to ~500-1000 chars with
[truncated]marker to prevent prompt bloat. - Attachment-only quotes: Quoted messages with no text but attachments silently pass through. A follow-up could add
[contained attachments only]note.
🔴 SUGGESTED CHANGES
- Must rebase — PR has merge conflicts with main. ~20 commits landed on main since PR opened. Contributor needs to rebase and resolve conflicts.
Reviewed by 超渡法師 🔃 chaodu Backlog triage
chaodu-agent
left a comment
There was a problem hiding this comment.
🟡 Conditional Approve — Clean feature, MUST rebase first
📋 Baseline Check (Step 0)
Checked main branch first: no reply/quote handling exists. This feature is 100% net-new. The PR adds resolve_referenced_message() and format_quote_context() right after resolve_mentions() in the message handler. No overlap or regression risk with existing code.
🧭 Four-Question Framework
1. What problem does this solve?
When users reply to a message in Discord, the bot currently has no idea what message is being referenced. This PR injects the quoted message content into the agent prompt so the bot can understand conversational context from replies.
2. How does it solve it?
Two new functions in src/discord.rs (+72 lines):
resolve_referenced_message()— async I/O to fetch the referenced message (tries cache first, falls back to HTTP)format_quote_context()— pure formatting function that prepends> @author: contentto the prompt
Inserted right after resolve_mentions() in the message handler pipeline.
3. What was considered?
- Correct serenity 0.12 types:
Box<Message>deref,message_reference.channel_idisChannelId,message_idisOption<MessageId> resolve_mentions()is applied to quoted content too — good attention to detail- Graceful degradation: if HTTP fallback fails, the message is processed without quote context (no crash)
- 4 unit tests covering edge cases
4. Is this the best approach?
The separation of async I/O and pure formatting is clean and testable. The approach is solid. A few improvements to consider (see traffic light below).
🚦 Traffic Light
🟢 INFO — Clean function separation (async I/O vs pure formatting) makes this easy to test and maintain.
🟢 INFO — Applying resolve_mentions() to quoted content prevents raw <@UID> leaking into the prompt. Nice catch.
🟢 INFO — 4 unit tests with good edge case coverage.
🔴 SUGGESTED CHANGES — PR is in CONFLICTING state. Many PRs have merged since April 22. Please rebase against main and resolve conflicts before this can be merged.
🟡 NIT — No length cap on quoted content. A very long quoted message could bloat the prompt significantly. Consider truncating to a reasonable limit (e.g., 500–1000 chars) with a [truncated] indicator.
🟡 NIT — Attachment-only quotes (no text content) silently pass through as empty. Consider adding a placeholder like [attachment] so the agent knows a quote existed.
Review by 超渡法師 🙏 — rebase to unblock merge!
Quoted message content was injected into the prompt without running resolve_mentions(), leaking raw Discord mention markup (<@BOT_ID>, <@&ROLE_ID>) into the LLM prompt. Apply the same resolve_mentions() pass used for the user's own message content.
Quoted messages longer than 1500 bytes are now truncated with a '… [truncated]' marker to prevent prompt bloat. Uses a UTF-8-safe truncation helper that respects char boundaries. Addresses reviewer feedback on openabdev#527.
chaodu-agent
left a comment
There was a problem hiding this comment.
APPROVE ✅ — All previous concerns addressed. Clean, focused feature ready to merge.
📋 Baseline Check (Step 0)
Checked main branch: no reply/quote handling exists. Feature is 100% net-new. No overlap with recent merges.
🧭 Four-Question Framework
1. What problem does this solve?
When users reply to a message in Discord, the bot loses the quoted message context. Prompts like "summarize this" become ambiguous because the agent has no idea what "this" refers to.
2. How does it solve it?
Three new functions in src/discord.rs (+108 lines):
resolve_referenced_message()— async, prefers gateway-providedreferenced_message(zero cost), falls back to HTTP APIformat_quote_context()— pure formatting, prepends[Quoted message from @author]blocktruncate_utf8()— UTF-8 safe truncation at 1500 bytes with… [truncated]marker
Inserted after resolve_mentions(), before the empty-check gate. Non-reply messages completely unaffected.
3. What was considered?
- Gateway-first + HTTP fallback is the correct serenity 0.12 pattern
resolve_mentions()applied to quoted content too — prevents raw<@UID>leaking into prompt- Graceful degradation: HTTP fallback failure logs warning, continues without quote
- Content length cap at 1500 bytes with UTF-8 char boundary safety
4. Is this the best approach?
Yes. Clean separation of async I/O and pure formatting. Well-tested (6 unit tests including UTF-8 boundary edge case). All 9 CI checks pass.
🚦 Traffic Light
🟢 INFO — Contributor addressed all previous review feedback:
- ✅ Rebased against main — now MERGEABLE (was CONFLICTING)
- ✅ Added 1500-byte truncation with UTF-8 safe boundary (was NIT in previous reviews)
- ✅
resolve_mentions()applied to quoted content (CHC-Agent fix) - ✅ 6 unit tests with good edge case coverage (up from 4)
- ✅ All 9 CI checks pass
🟢 INFO — Cow<str> usage in truncate_utf8() avoids unnecessary allocation when content is under the limit.
🟡 NIT — Attachment-only quotes (no text content) silently pass through as empty. A follow-up could add a placeholder like [attachment]. Non-blocking.
Review by 超渡法師 🙏
🟢 LGTM — ApproveClean, well-scoped feature that fills a real gap. No concerns blocking merge. 1. What problem does it solve?When a Discord user replies to (quotes) a message, the bot previously only forwarded the new message text. The agent had no idea what "this" or "that" referred to. This PR injects the quoted message content into the prompt so the agent gets full conversational context. Closes #339. 2. How does it solve it?Three new functions in
Injection point: right after Purely additive: +116 lines, 0 deletions, single file. 3. What was considered?
4. Is this the best approach?Yes, this is the right approach for the scope of the problem:
No concerns about the design. Traffic Light🟢 INFO — Gateway-first strategy in 🟢 INFO — 🟢 INFO — UTF-8-safe truncation with 🟢 INFO — Good test coverage: 6 tests covering normal, empty, multiline, truncation, and UTF-8 boundary cases. 🟡 NIT — The 🟡 NIT — Reviewed by 超渡法師 🪬 |
Closing in favor of ADR: Context-Aware Token (#716)Thanks for the clean implementation, @ChunHao-dev! During maintainer review, we discussed the broader design direction and concluded:
We've opened PR #716 with an ADR proposing a context-aware token pattern that lets agents pull context when they judge it necessary. This supersedes the always-on approach in this PR. Your work here directly informed the ADR design — the Closing this PR in favor of #716. 🙏 |
Summary
When a user replies to (quotes) a message in a Discord thread, the bot only sends the new message text to the agent. The quoted/referenced message content is lost — the agent has no idea what "this" refers to.
This PR reads
msg.referenced_messageand prepends the quoted content to the prompt:Implementation
resolve_referenced_message()— prefers gateway-providedreferenced_message(zero cost); falls back to HTTP API call viamessage_referenceif the gateway didn't include the full messageformat_quote_context()— pure function that formats the quote blockresolve_mentions(), before the prompt is sent to the routerresolve_referenced_messagereturnsNone)Testing
format_quote_context()(normal, empty content, empty prompt, multiline)Closes #339
Discord Discussion URL: https://discord.com/channels/1491295327620169908/1496538680142069800